package de.islanders.shared;

import java.util.LinkedList;

public class RandomObject<T extends Number> {
	private static int indexCount;
	private static LinkedList<Integer> skips;
	
	static {
		indexCount = 0;
		skips = new LinkedList<Integer>();
	}
	
	private int index;
	private ValueSpan<T> inner;
	private ValueSpan<Double> outer;
	private double weight;
	private double relativeWeight;
	private final Class<T> type;
	
	public RandomObject(T iLow, T iHigh, double oLow, double oHigh, Class<T> type) {
		this(iLow, iHigh, oLow, oHigh, 0, type);
	}

	public RandomObject(T iLow, T iHigh, Class<T> type) {
		this(iLow, iHigh, 0d, 0d, 0, type);
	}
	
	public RandomObject(T iLow, T iHigh, double weight, Class<T> type) {
		this(iLow, iHigh, 0d, 0d, weight, type);
	}
	
	public RandomObject(T iLow, T iHigh, double oLow, double oHigh, double weight, Class<T> type) {
		if(skips.isEmpty()){
			this.index = ++indexCount;
		} else {
			this.index = skips.pollFirst();
		}
		
		this.inner = new ValueSpan<T>(iLow, iHigh);
		this.outer = new ValueSpan<Double>(oLow, oHigh);
		this.type = type;
		this.weight = weight;
		if(this.weight > 0) {
			this.relativeWeight = this.weight;
		}
	}
	
	public void dispose() {
		skips.add(this.index);
	}
	
	public int getIndex() {
		return index;
	}
	
	public ValueSpan<T> getInner() {
		return inner;
	}
	
	public void setInner(T newLow, T newHigh) {
		this.inner = new ValueSpan<T>(newLow, newHigh);
	}
	
	public ValueSpan<? extends Number> getOuter() {
		return outer;
	}
	
	public void setOuter(double newLow, double newHigh) {
		this.outer = new ValueSpan<Double>(newLow, newHigh);
	}
	
	public double getRange() {
		return this.outer.getHigh() - this.outer.getLow();
	}
	
	public Class<T> getType() {
		return type;
	}
	
	public double getWeight() {
		return weight;
	}
	
	public void setWeight(double weight) {
		this.weight = weight;
	}
	
	public boolean hasWeight() {
		return weight > 0;
	}
	
	public double getRelativeWeight() {
		return relativeWeight;
	}
	
	public void setRelativeWeight(double relativeWeight) {
		this.relativeWeight = relativeWeight;
	}
	
	public boolean inRangeInner(T tgt) {
		return ((inner.getLow().doubleValue() <= tgt.doubleValue()) &&
				(inner.getHigh().doubleValue() > tgt.doubleValue()));
	} 
	
	public boolean inRangeOuter(double tgt) {
		return ((outer.getLow() <= tgt) && (outer.getHigh() > tgt));
	}
	
	@Override
	public String toString() {
		String msg = String.format("RandomObject<%s>:\tindex(%s), weight(%s), relative weight(%s), inner(%s), outer(%s),\trange(%s)", 
				"%s",
				"%d",
				"%.3f",
				"%.3f",
				(type.equals(Double.class) || type.equals(Float.class)) ? "%.2f-%.2f" : "%d-%d",
				"%.3f-%.3f",
				"%.1f");
		msg = String.format(msg,
				this.type.getSimpleName(),
				this.index,
				this.weight,
				this.relativeWeight,
				this.inner.getLow(), this.inner.getHigh(),
				this.outer.getLow(), this.outer.getHigh(),
				this.getRange());
		return msg;
	}
}